package com.example.sourcecodelearning.modules.designpattern.singleton;

import lombok.Getter;

/**
 * @author yuzhenxin
 * @date 2023/8/3
 */
public class Singleton {
    public static void main(String[] args) {
        System.out.println(Hungry.getInstance() == Hungry.getInstance());
        System.out.println(LazySingleton1.getInstance() == LazySingleton1.getInstance());
        System.out.println(LazySingleton2.getInstance() == LazySingleton2.getInstance());
        System.out.println(EnumSingleton.getInstance() == EnumSingleton.getInstance());
    }
}

class Hungry {
    private Hungry() {
    }

    @Getter
    private static final Hungry instance = new Hungry();

}

/**
 * 懒汉式1：双关检测
 */
class LazySingleton1 {
    private LazySingleton1() {
    }

    // 懒汉线程安全
    // 1. memory = allocate(); //分配内存空间（留位置给新同学）
    // 2. instance(memory); // 初始化对象（新同学到达）
    // 3. instance = memory; // 设置instance指向该内存地址，instance!=null（看向该位置）
    // 由于2和3没有数据依赖关系，多线程下可被重排，此时instance != null，但是对象还未初始化完成！因此需加volatile禁止指令重排
    private static volatile LazySingleton1 instance2;

    public static LazySingleton1 getInstance() {
        // 双锁检查，线程安全
        if (instance2 == null) {
            synchronized (LazySingleton1.class) {
                if (instance2 == null) {
                    instance2 = new LazySingleton1();
                }
            }
        }
        return instance2;
    }
}

/**
 * 懒汉2：静态内部类获取
 */
class LazySingleton2 {
    private LazySingleton2() {
    }

    public static LazySingleton2 getInstance() {
        // 此处才创建INSTANCE
        return Inner.INSTANCE;
    }

    // 通过方法获取该静态实例
    // 加载内部类才创建INSTANCE对象
    private static class Inner {
        private static final LazySingleton2 INSTANCE = new LazySingleton2();
    }
}

/**
 * 枚举方式进行实例化，是线程安全的，此种方式也是线程最安全的
 */
class EnumSingleton {

    private EnumSingleton() {
    }

    public static EnumSingleton getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    @Getter
    private enum Singleton {
        INSTANCE;

        private final EnumSingleton instance;

        // JVM保证这个方法绝对只调用一次
        Singleton() {
            instance = new EnumSingleton();
        }

    }
}